using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;

namespace BardsTaleMapViewer {

  public static class ExtensionMethods {
    public static void Swap<T> (ref T lhs, ref T rhs) {
      T temp;
      temp = lhs;
      lhs = rhs;
      rhs = temp;
    }

    public static void VstClearOutputWindow () {
      if (!Debugger.IsAttached)
        return;

      Application.DoEvents ();
      Thread.Sleep (100);
      //EnvDTE80.DTE2 ide = (EnvDTE80.DTE2) Marshal.GetActiveObject ("VisualStudio.DTE.9.0");
      //ide.ExecuteCommand ("Edit.ClearOutputWindow", "");
      //Marshal.ReleaseComObject (ide);
    }
  }

  public static class StringExtension {
    public static StringBuilder AppendLineFormat (this StringBuilder builder, string format, params object [] arguments) {
      string value = String.Format (format, arguments);
      builder.AppendLine (value);
      return builder;
    }
  }

  public static class RectangleExtension {

    public static Point Center (this Rectangle rectangle) {
      return (new Point ((rectangle.Left + rectangle.Right) >> 1, (rectangle.Top + rectangle.Bottom) >> 1));
    }
  }

  public static class SizeFExtension {

    public static Size ToSizeCeiling (this SizeF sizeF) {
      return (new Size ((int) Math.Ceiling (sizeF.Width), (int) Math.Ceiling (sizeF.Height)));
    }
  }

  public static class BitmapExtension {
    //public unsafe static Rectangle GetBoundingBox (this Bitmap b) {
    //  Rectangle r = Rectangle.Empty;
    //  BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);

    //  //UInt32* p = (UInt32*) bitmapData.Scan0.ToPointer ();
    //  byte* p1 = (byte*) bitmapData.Scan0.ToPointer ();
    //  for (int y = 0; y < b.Height; y++) {
    //    //long offset = y * bitmapData.Stride;
    //    byte* p2 = p1;
    //    for (int x = 0; x < b.Width; x++) {
    //      //UInt32 color = p [x];
    //      byte byte1 = p2 [0];
    //      byte byte2 = p2 [1];
    //      byte byte3 = p2 [2];
    //      byte byte4 = p2 [3];
    //      p2 += 4;
    //    }
    //    //p += bitmapData.Stride / 4;
    //    p1 += bitmapData.Stride;
    //  }

    //  b.UnlockBits (bitmapData);
    //  return (r);
    //}

    public static Rectangle GetBoundingBox (this Bitmap b) {
      Rectangle result = Rectangle.Empty;

      int xMin = int.MaxValue;
      int xMax = int.MinValue;
      int yMin = int.MaxValue;
      int yMax = int.MinValue;

      BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);
      int offset1 = 0;
      for (int y = 0; y < bitmapData.Height; y++) {
        //long offset = y * bitmapData.Stride;
        int offset2 = offset1;
        bool yDone = false;
        for (int x = 0; x < bitmapData.Width; x++) {
          uint color = (uint) Marshal.ReadInt32 (bitmapData.Scan0, offset2);
          if ((color & 0xFF000000) != 0) {
            if (x < xMin) {
              xMin = x;
            }
            if (x > xMax) {
              xMax = x;
            }
            if (!yDone) {
              if (y < yMin) {
                yMin = y;
              }
              if (y > yMax) {
                yMax = y;
              }
            }
          }

          offset2 += 4;
        }
        //p += bitmapData.Stride / 4;
        offset1 += bitmapData.Stride;
      }
      b.UnlockBits (bitmapData);

      if (xMin <= xMax) {
        if (yMin <= yMax) {
          result = new Rectangle (xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
        }
        else {
          throw new Exception ("?");
        }
      }

      return (result);
    }

    public static Bitmap ExtractShape (this Bitmap b) {
      Rectangle rectangle = b.GetBoundingBox ();
      if (rectangle == Rectangle.Empty) {
        return (b);
      }
      if (rectangle.Size == b.Size) {
        return (b);
      }
      Bitmap b2 = new Bitmap (rectangle.Width, rectangle.Height, b.PixelFormat);
      using (Graphics g = Graphics.FromImage (b2)) {
        g.DrawImage (b, 0, 0, rectangle, GraphicsUnit.Pixel);
      }
      return (b2);
    }

    public static Bitmap FitInto (this Bitmap b, int w, int h) {
      Bitmap dst = new Bitmap (16, 16, PixelFormat.Format32bppPArgb);
      using (Graphics g = Graphics.FromImage (dst)) {
        float zoom = Math.Min (Math.Min (((float) w) / b.Width, ((float) h) / b.Height), 1.0f);
        //g.PixelOffsetMode = PixelOffsetMode.HighQuality;
        //g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.InterpolationMode = InterpolationMode.NearestNeighbor;
        int newWidth = (int) (b.Width * zoom);
        int newHeight = (int) (b.Height * zoom);
        int x = (16 - newWidth) / 2;
        int y = (16 - newHeight) / 2;
        g.DrawImage (b, x, y, newWidth, newHeight);
      }
      return (dst);
    }

    public static Bitmap Copy (this Bitmap b) {
      return (b.Copy (PixelFormat.Format32bppPArgb));
    }

    public static Bitmap Copy (this Bitmap b, PixelFormat pixelFormat) {
      //Bitmap dst = b.Clone (new Rectangle (0, 0, b.Width, b.Height), PixelFormat.Format32bppPArgb);
      Bitmap dst = new Bitmap (b.Width, b.Height, pixelFormat);
      using (Graphics g = Graphics.FromImage (dst)) {
        g.DrawImage (b, 0, 0);
      }
      return (dst);
    }

    public static Bitmap ExtractBitmap (this Bitmap b, int x, int y, int width, int height) {
      Rectangle rectangle = new Rectangle (x, y, width, height);
      Bitmap b2 = new Bitmap (rectangle.Width, rectangle.Height, b.PixelFormat);
      using (Graphics g = Graphics.FromImage (b2)) {
        g.DrawImage (b, 0, 0, rectangle, GraphicsUnit.Pixel);
      }
      return (b2);
    }

    public static bool IsSame (this Bitmap b1, Bitmap b2) {
      bool result = true;
      BitmapData bitmapData1 = b1.LockBits (new Rectangle (0, 0, b1.Width, b1.Height), ImageLockMode.ReadOnly, b1.PixelFormat);
      BitmapData bitmapData2 = b2.LockBits (new Rectangle (0, 0, b2.Width, b2.Height), ImageLockMode.ReadOnly, b2.PixelFormat);
      int offset1 = 0;
      for (int y = 0; (y < bitmapData1.Height) && (result); y++) {
        int offset2 = offset1;
        for (int x = 0; x < (bitmapData1.Width) && (result); x++) {
          uint color1 = (uint) Marshal.ReadInt32 (bitmapData1.Scan0, offset2);
          uint color2 = (uint) Marshal.ReadInt32 (bitmapData2.Scan0, offset2);
          if (color1 != color2)
            result = false;
          offset2 += 4;
        }
        offset1 += bitmapData1.Stride;
      }
      b1.UnlockBits (bitmapData1);
      b2.UnlockBits (bitmapData2);
      return (result);
    }

    public static void ReplaceColor (this Bitmap b, Color src, Color dst, bool ignoreAlphaInCompare) {
      uint colorSrc = (uint) src.ToArgb ();
      uint colorDst = (uint) dst.ToArgb ();
      BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, b.PixelFormat);
      int offset1 = 0;
      int bitmapWidth = bitmapData.Width;
      int bitmapHeight = bitmapData.Height;
      IntPtr pSrc = bitmapData.Scan0;
      int stride = bitmapData.Stride;
      for (int y = 0; y < bitmapHeight; y++) {
        int offset2 = offset1;
        for (int x = 0; x < bitmapWidth; x++) {
          uint color = (uint) Marshal.ReadInt32 (pSrc, offset2);
          if (ignoreAlphaInCompare) {
            if ((color & 0x00FFFFFF) == (colorSrc & 0x00FFFFFF)) {
              colorDst = (colorDst & 0x00FFFFFF) | (colorSrc & 0xFF000000);
              Marshal.WriteInt32 (pSrc, offset2, (int) colorDst);
            }
          }
          else {
            if (color == colorSrc)
              Marshal.WriteInt32 (pSrc, offset2, (int) colorDst);
          }
          offset2 += 4;
        }
        offset1 += stride;
      }
      b.UnlockBits (bitmapData);
    }

    //public unsafe static void ReplaceIndexedColor (this Bitmap b, int colorSrc, int colorDst) {
    //  BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, b.PixelFormat);
    //  int offset1 = 0;
    //  int bitmapWidth = bitmapData.Width;
    //  int bitmapHeight = bitmapData.Height;
    //  byte* pSrc = (byte*) bitmapData.Scan0.ToPointer ();
    //  int stride = bitmapData.Stride;
    //  for (int y = 0; y < bitmapHeight; y++) {
    //    int offset2 = offset1;
    //    for (int x = 0; x < bitmapWidth; x++) {
    //      int color = pSrc [offset2];
    //      if (color == colorSrc)
    //        pSrc [offset2] = (byte) colorDst;
    //      offset2++;
    //    }
    //    offset1 += stride;
    //  }
    //  b.UnlockBits (bitmapData);
    //}

    public static void Colorize (this Bitmap b, Color colorizeColor) {
      uint colorize = (uint) colorizeColor.ToArgb ();
      BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, b.PixelFormat);
      int offset1 = 0;
      int bitmapWidth = bitmapData.Width;
      int bitmapHeight = bitmapData.Height;
      IntPtr pSrc = bitmapData.Scan0;
      int stride = bitmapData.Stride;
      for (int y = 0; y < bitmapHeight; y++) {
        int offset2 = offset1;
        for (int x = 0; x < bitmapWidth; x++) {
          uint color = (uint) Marshal.ReadInt32 (pSrc, offset2);
          if ((color &= 0xFF000000) != 0) {
            uint colorDst = (colorize & 0x00FFFFFF) | (color & 0xFF000000);
            Marshal.WriteInt32 (pSrc, offset2, (int) colorDst);
          }
          offset2 += 4;
        }
        offset1 += stride;
      }
      b.UnlockBits (bitmapData);
    }

    //public unsafe static void ColorizeIndexed (this Bitmap b, int colorizeColor, int colorTransparent) {
    //  BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, b.PixelFormat);
    //  int offset1 = 0;
    //  int bitmapWidth = bitmapData.Width;
    //  int bitmapHeight = bitmapData.Height;
    //  byte* pSrc = (byte*) bitmapData.Scan0.ToPointer ();
    //  int stride = bitmapData.Stride;
    //  for (int y = 0; y < bitmapHeight; y++) {
    //    int offset2 = offset1;
    //    for (int x = 0; x < bitmapWidth; x++) {
    //      int color = pSrc [offset2];
    //      if (color != colorTransparent)
    //        pSrc [offset2] = (byte) colorizeColor;
    //      offset2++;
    //    }
    //    offset1 += stride;
    //  }
    //  b.UnlockBits (bitmapData);
    //}

    public static void ApplyMask (this Bitmap b, byte [] mask) {
      BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, b.PixelFormat);
      int maskWidthBytes = (int) Math.Ceiling (bitmapData.Width / 8.0);

      if (mask.Length != (maskWidthBytes * bitmapData.Height))
        throw new Exception ();
      int offset1 = 0;
      for (int y = 0; y < bitmapData.Height; y++) {
        int offset2 = offset1;
        int offsetMask = maskWidthBytes * y;
        int bit = 7;
        for (int x = 0; x < bitmapData.Width; x++) {
          uint color = (uint) Marshal.ReadInt32 (bitmapData.Scan0, offset2);
          if (((mask [offsetMask] >> bit) & 1) == 1)
            Marshal.WriteInt32 (bitmapData.Scan0, offset2, (int) (color & 0x00000000));
          // or this one to keep transparent data
          //Marshal.WriteInt32 (bitmapData.Scan0, offset2, (int) (color & 0x00FFFFFF));
          bit--;
          if (bit == -1) {
            offsetMask++;
            bit = 7;
          }
          offset2 += 4;
        }
        offset1 += bitmapData.Stride;
      }

      b.UnlockBits (bitmapData);
    }

    public static int CountColor (this Bitmap b, Color color) {
      uint colorHex = (uint) color.ToArgb ();
      BitmapData bitmapData = b.LockBits (new Rectangle (0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);
      int nb = 0;
      int offset1 = 0;
      for (int y = 0; y < bitmapData.Height; y++) {
        int offset2 = offset1;
        for (int x = 0; x < bitmapData.Width; x++) {
          uint colorHex2 = (uint) Marshal.ReadInt32 (bitmapData.Scan0, offset2);
          if (colorHex == colorHex2) {
            nb++;
          }
          offset2 += 4;
        }
        offset1 += bitmapData.Stride;
      }
      b.UnlockBits (bitmapData);
      return (nb);
    }
  }

  public static class BitmapDataExtension {

    public static int GetOffset (this BitmapData bitmapData, int x, int y) {
      return (y * bitmapData.Stride + x * 4);
    }

    public static int GetPixel (this BitmapData bitmapData, int offset) {
      int color = Marshal.ReadInt32 (bitmapData.Scan0, offset);
      return (color);
    }

    public static void SetPixel (this BitmapData bitmapData, int offset, int color) {
      Marshal.WriteInt32 (bitmapData.Scan0, offset, color);
    }

    public static int GetPixel (this BitmapData bitmapData, int x, int y) {
      int color = Marshal.ReadInt32 (bitmapData.Scan0, y * bitmapData.Stride + x * 4);
      return (color);
    }

    public static void SetPixel (this BitmapData bitmapData, int x, int y, int color) {
      Marshal.WriteInt32 (bitmapData.Scan0, y * bitmapData.Stride + x * 4, color);
    }

    /**
     * Searches for value that is not equal to given color value.
     */
    public static int ScanRight (this BitmapData bitmapData,
        int x,			//!< x-coordinate.
        int y,			//!< y-coordinate.
        int xmax,		//!< x-maximum coordinate
        int dwValue	//!< Specifies color value.
      ) {
      //ASSERT_VALID(this);
      //ASSERT(m_pDib != NULL);

      if (xmax < 0)
        return ++xmax;

      int nWidth = bitmapData.Width;

      if (x < 0 || nWidth <= x || y < 0 || Math.Abs (bitmapData.Height) <= y)
        return ++xmax;

      if (nWidth <= xmax)
        return ++xmax;

      int offset = bitmapData.GetOffset (x, y);
      for (; x <= xmax; ++x, offset += 4) {
        if (dwValue != bitmapData.GetPixel (offset))
          break;
      }
      return x;
    }

    /**
     * Searches for value that is equal to given color value.
     */
    public static int SearchRight (this BitmapData bitmapData,
        int x,			//!< x-coordinate.
        int y,			//!< y-coordinate.
        int xmax,		//!< x-maximum coordinate
        int dwValue	//!< Specifies color value.
      ) {
      //ASSERT_VALID (this);
      //ASSERT (m_pDib != NULL);

      if (xmax < 0)
        return ++xmax;

      int nWidth = bitmapData.Width;

      if (x < 0 || nWidth <= x || y < 0 || Math.Abs (bitmapData.Height) <= y)
        return ++xmax;

      if (nWidth <= xmax)
        return ++xmax;

      int offset = bitmapData.GetOffset (x, y);
      for (; x <= xmax; ++x, offset += 4) {
        if (dwValue == bitmapData.GetPixel (offset))
          break;
      }
      return x;
    }

    /**
     * Searches for value that is not equal to given color value.
     */
    public static int ScanLeft (this BitmapData bitmapData,
        int x,			//!< x-coordinate.
        int y,			//!< y-coordinate.
        int xmin,		//!< x-minimum coordinate
        int dwValue	//!< Specifies color value.
      ) {
      //ASSERT_VALID (this);
      //ASSERT (m_pDib != NULL);

      if (xmin < 0)
        return --xmin;

      int nWidth = bitmapData.Width;

      if (x < 0 || nWidth <= x || y < 0 || Math.Abs (bitmapData.Height) <= y)
        return --xmin;

      if (nWidth <= xmin)
        return --xmin;

      int offset = bitmapData.GetOffset (x, y);
      for (; x >= xmin; --x, offset -= 4) {
        if (dwValue != bitmapData.GetPixel (offset))
          break;
      }
      return x;
    }

    /**
     * Searches for value that is equal to given color value.
     */
    public static int SearchLeft (this BitmapData bitmapData,
        int x,			//!< x-coordinate.
        int y,			//!< y-coordinate.
        int xmin,		//!< x-minimum coordinate
        int dwValue	//!< Specifies color value.
      ) {
      //ASSERT_VALID(this);
      //ASSERT(m_pDib != NULL);

      if (xmin < 0)
        return --xmin;

      int nWidth = bitmapData.Width;

      if (x < 0 || nWidth <= x || y < 0 || Math.Abs (bitmapData.Height) <= y)
        return --xmin;

      if (nWidth <= xmin)
        return --xmin;

      int offset = bitmapData.GetOffset (x, y);
      for (; x >= xmin; --x, offset -= 4) {
        if (dwValue == bitmapData.GetPixel (offset))
          break;
      }
      return x;
    }

    //public unsafe static void BlitFont (this Bitmap bitmapDst, Bitmap bitmapSrc, int xSrc, int ySrc, int widthSrc, int heightSrc, int xDst, int yDst, Color colorizeColor) {
    //  int bitmapWidthSrc = bitmapSrc.Width;
    //  int bitmapHeightSrc = bitmapSrc.Height;
    //  int bitmapWidthDst = bitmapDst.Width;
    //  int bitmapHeightDst = bitmapDst.Height;

    //  int xEndSrc = xSrc + widthSrc;
    //  int yEndSrc = ySrc + heightSrc;
    //  if (xEndSrc > bitmapWidthSrc)
    //    xEndSrc = bitmapWidthSrc;
    //  if (yEndSrc > bitmapHeightSrc)
    //    yEndSrc = bitmapHeightSrc;

    //  int xEndDst = xDst + widthSrc;
    //  int yEndDst = yDst + heightSrc;
    //  if (xEndDst > bitmapWidthDst)
    //    xEndDst = bitmapWidthDst;
    //  if (yEndDst > bitmapHeightDst)
    //    yEndDst = bitmapHeightDst;

    //  if (bitmapSrc.PixelFormat != PixelFormat.Format32bppArgb)
    //    throw new NotSupportedException ();
    //  if (bitmapDst.PixelFormat != PixelFormat.Format32bppArgb)
    //    throw new NotSupportedException ();
    //  int bytesPerPixel = 4;

    //  BitmapData bmdSrc = bitmapSrc.LockBits (new Rectangle (0, 0, bitmapWidthSrc, bitmapHeightSrc), ImageLockMode.ReadOnly, bitmapSrc.PixelFormat);
    //  BitmapData bmdDst = bitmapDst.LockBits (new Rectangle (0, 0, bitmapWidthDst, bitmapHeightDst), ImageLockMode.WriteOnly, bitmapDst.PixelFormat);

    //  int strideSrc = bmdSrc.Stride;
    //  int strideDst = bmdDst.Stride;

    //  byte* pSrc = (byte*) bmdSrc.Scan0.ToPointer ();
    //  byte* pDst = (byte*) bmdDst.Scan0.ToPointer ();
    //  pSrc += ySrc * strideSrc + xSrc * bytesPerPixel;
    //  pDst += yDst * strideDst + xDst * bytesPerPixel;

    //  bool colorize = (colorizeColor != Color.Transparent);
    //  for (; (ySrc < yEndSrc) && (yDst < yEndDst); ySrc++, yDst++) {
    //    if ((ySrc >= 0) && (yDst >= 0)) {
    //      byte* pSrcTemp = pSrc;
    //      byte* pDstTemp = pDst;
    //      for (int xSrcTemp = xSrc, xDstTemp = xDst; (xSrcTemp < xEndSrc) && (xDstTemp < xEndDst); xSrcTemp++, xDstTemp++) {

    //        if ((xSrc >= 0) && (xDst >= 0)) {
    //          // BGRA
    //          if (colorize) {
    //            if (pSrcTemp [3] != 0x00) {
    //              pDstTemp [0] = colorizeColor.B;
    //              pDstTemp [1] = colorizeColor.G;
    //              pDstTemp [2] = colorizeColor.R;
    //            }
    //          }
    //          else {
    //            if (pSrcTemp [3] != 0x00) {
    //              pDstTemp [0] = pSrcTemp [0];
    //              pDstTemp [1] = pSrcTemp [1];
    //              pDstTemp [2] = pSrcTemp [2];
    //            }
    //          }
    //        }
    //        pSrcTemp += bytesPerPixel;
    //        pDstTemp += bytesPerPixel;
    //      }
    //    }
    //    pSrc += strideSrc;
    //    pDst += strideDst;
    //  }

    //  bitmapSrc.UnlockBits (bmdSrc);
    //  bitmapDst.UnlockBits (bmdDst);


    //  //public static int MergeColors (int backgroundColor, int foregroundColor) {
    //  //  final byte ALPHA_CHANNEL = 24;
    //  //  final byte RED_CHANNEL = 16;
    //  //  final byte GREEN_CHANNEL = 8;
    //  //  final byte BLUE_CHANNEL = 0;

    //  //  final double ap1 = (double) (backgroundColor >> ALPHA_CHANNEL & 0xff) / 255d;
    //  //  final double ap2 = (double) (foregroundColor >> ALPHA_CHANNEL & 0xff) / 255d;
    //  //  final double ap = ap2 + (ap1 * (1 - ap2));

    //  //  final double amount1 = (ap1 * (1 - ap2)) / ap;
    //  //  final double amount2 = amount1 / ap;

    //  //  int a = ((int) (ap * 255d)) & 0xff;

    //  //  int r = ((int) (((float) (backgroundColor >> RED_CHANNEL & 0xff) * amount1) +
    //  //          ((float) (foregroundColor >> RED_CHANNEL & 0xff) * amount2))) & 0xff;
    //  //  int g = ((int) (((float) (backgroundColor >> GREEN_CHANNEL & 0xff) * amount1) +
    //  //          ((float) (foregroundColor >> GREEN_CHANNEL & 0xff) * amount2))) & 0xff;
    //  //  int b = ((int) (((float) (backgroundColor & 0xff) * amount1) +
    //  //          ((float) (foregroundColor & 0xff) * amount2))) & 0xff;

    //  //  return a << ALPHA_CHANNEL | r << RED_CHANNEL | g << GREEN_CHANNEL | b << BLUE_CHANNEL;
    //  //}


    //  //unsafe {
    //  //  BitmapData bitmapData = processedBitmap.LockBits (new Rectangle (0, 0, processedBitmap.Width, processedBitmap.Height), ImageLockMode.ReadWrite, processedBitmap.PixelFormat);
    //  //  int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize (processedBitmap.PixelFormat) / 8;
    //  //  int heightInPixels = bitmapData.Height;
    //  //  int widthInBytes = bitmapData.Width * bytesPerPixel;
    //  //  byte* ptrFirstPixel = (byte*) bitmapData.Scan0;

    //  //  for (int y = 0; y < heightInPixels; y++) {
    //  //    byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);
    //  //    for (int x = 0; x < widthInBytes; x = x + bytesPerPixel) {
    //  //      int oldBlue = currentLine [x];
    //  //      int oldGreen = currentLine [x + 1];
    //  //      int oldRed = currentLine [x + 2];

    //  //      // calculate new pixel value
    //  //      currentLine [x] = (byte) oldBlue;
    //  //      currentLine [x + 1] = (byte) oldGreen;
    //  //      currentLine [x + 2] = (byte) oldRed;
    //  //    }
    //  //  }
    //  //  processedBitmap.UnlockBits (bitmapData);
    //  //}
    //}
  }

  public static class GraphicsExtension {
    public static ColorMatrix _colorMatrixAlpha;
    public static ImageAttributes _imageAttributesAlpha;
    public static ColorMatrix _colorMatrixColorize;
    public static ImageAttributes _imageAttributesColorize;

    static GraphicsExtension () {
      _colorMatrixAlpha = new ColorMatrix ();
      _imageAttributesAlpha = new ImageAttributes ();
      _colorMatrixColorize = new ColorMatrix ();
      _imageAttributesColorize = new ImageAttributes ();
    }

    public static RectangleF MeasureString2 (this Graphics g, string text, Font font) {
      //SizeF space1 = MeasureDisplayStringWidth (g, "A A", font);
      //SizeF space2 = MeasureDisplayStringWidth (g, "A  A", font);
      //float spaceWidth = space2 - space1;
      //SizeF size = SizeF.Empty;
      //foreach (char c in text) {
      //  //SizeF sizeC = g.MeasureString (c.ToString (), font);
      //  SizeF sizeC = g.MeasureString (c.ToString (), font);
      //  size.Width += sizeC.Width;
      //  if (size.Height < sizeC.Height)
      //    size.Height = sizeC.Height;
      //}
      //return (size);
      return (g.MeasureDisplayStringWidth (text, font));
    }

    public static Dictionary<Font, Point> _fontsPointDelta = new Dictionary<Font, Point> ();
    public static Dictionary<Font, Point> _fontsSizeDelta = new Dictionary<Font, Point> ();

    public static void AddDelta (this Font font, Point pointDelta, Point sizeDelta) {
      _fontsPointDelta.Add (font, pointDelta);
      _fontsSizeDelta.Add (font, sizeDelta);
    }

    public static RectangleF MeasureDisplayStringWidth (this Graphics graphics, string text, Font font) {
      System.Drawing.StringFormat format = new System.Drawing.StringFormat ();
      System.Drawing.RectangleF rect = new System.Drawing.RectangleF (0, 0, 1000, 1000);
      System.Drawing.CharacterRange [] ranges = { new System.Drawing.CharacterRange (0, text.Length) };
      //System.Drawing.Region [] regions = new System.Drawing.Region [1];

      format.SetMeasurableCharacterRanges (ranges);
      //format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.FitBlackBox;
      format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

      Region [] regions = graphics.MeasureCharacterRanges (text, font, rect, format);
      rect = regions [0].GetBounds (graphics);
      //SizeF sizeF2 = new SizeF (rect.Right + 1.0f, rect.Bottom + 1.0f);
      //SizeF sizeF = new SizeF (rect.Width + 2.0f, rect.Height + 2.0f);
      //SizeF sizeF = new SizeF (rect.Width, rect.Height);
      //if ((rect.Right + 1.0f) != (rect.Width + 2.0f))
      //  throw new Exception ();
      //SizeF sizeF = rect.Size;
      //sizeF.Width -= 4;
      //sizeF.Height--;
      //return (int) (rect.Right + 1.0f);
      //return (rect.Size);
      //Console.WriteLine ("[{0}] {1}", text, rect);
      //rect.Width--;
      //rect.Height--;

      if (_fontsPointDelta.ContainsKey (font)) {
        Point pointDelta = _fontsPointDelta [font];
        rect.X -= pointDelta.X;
        rect.Y -= pointDelta.Y;
      }

      if (_fontsSizeDelta.ContainsKey (font)) {
        Point sizeDelta = _fontsSizeDelta [font];
        rect.Width += sizeDelta.X;
        rect.Height += sizeDelta.Y;
      }

      return (rect);
    }

    public static void DrawImage (this Graphics g, Image image, int x, int y, float alpha) {
      _colorMatrixAlpha.Matrix33 = alpha;
      _imageAttributesAlpha.SetColorMatrix (_colorMatrixAlpha);
      g.DrawImage (image, new Rectangle (x, y, image.Width, image.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, _imageAttributesAlpha);
    }

    public static void DrawImage (this Graphics g, Image image, int x, int y, Color colorize) {
      _colorMatrixColorize.Matrix00 = colorize.R / 255f;
      _colorMatrixColorize.Matrix11 = colorize.G / 255f;
      _colorMatrixColorize.Matrix22 = colorize.B / 255f;
      _colorMatrixColorize.Matrix33 = colorize.A / 255f;
      _imageAttributesColorize.SetColorMatrix (_colorMatrixColorize);
      g.DrawImage (image, new Rectangle (x, y, image.Width, image.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, _imageAttributesColorize);
    }

    public static void DrawImage (this Graphics g, Image image, int x, int y, int zoom) {
      //g.DrawImage (b, x, y, (b.Width << ToolBox.ZoomShift) + 1, (b.Height << ToolBox.ZoomShift) + 1);
      //g.InterpolationMode = InterpolationMode.NearestNeighbor;
      //g.SmoothingMode = SmoothingMode.None;
      //g.PixelOffsetMode = PixelOffsetMode.None;
      GraphicsState gs = g.Save ();
      //PixelOffsetMode temp = g.PixelOffsetMode;
      g.PixelOffsetMode = PixelOffsetMode.Half;
      g.SmoothingMode = SmoothingMode.None;
      g.InterpolationMode = InterpolationMode.NearestNeighbor;
      g.DrawImage (image, x + 0.0f, y + 0.0f, (image.Width * zoom) + 0, (image.Height * zoom) + 0);
      //g.PixelOffsetMode = temp;
      g.Restore (gs);
    }

    public static void DrawImage (this Graphics g, Image image, int x, int y, int zoom, bool flipHorizontal) {
      //g.DrawImage (b, x, y, (b.Width << ToolBox.ZoomShift) + 1, (b.Height << ToolBox.ZoomShift) + 1);
      //g.InterpolationMode = InterpolationMode.NearestNeighbor;
      //g.SmoothingMode = SmoothingMode.None;
      //g.PixelOffsetMode = PixelOffsetMode.None;
      GraphicsState gs = g.Save ();
      //PixelOffsetMode temp = g.PixelOffsetMode;
      g.PixelOffsetMode = PixelOffsetMode.Half;
      g.SmoothingMode = SmoothingMode.None;
      g.InterpolationMode = InterpolationMode.NearestNeighbor;
      g.DrawImage (image, x + (flipHorizontal ? image.Width * zoom : 0), y, image.Width * zoom * (flipHorizontal ? -1 : 1), image.Height * zoom);
      //g.PixelOffsetMode = temp;
      g.Restore (gs);
    }

    public static void DrawImageZoomed (this Graphics g, Image image, int x, int y, int zoom) {
      //g.DrawImage (b, x, y, (b.Width << ToolBox.ZoomShift) + 1, (b.Height << ToolBox.ZoomShift) + 1);
      //g.InterpolationMode = InterpolationMode.NearestNeighbor;
      //g.SmoothingMode = SmoothingMode.None;
      //g.PixelOffsetMode = PixelOffsetMode.None;
      GraphicsState gs = g.Save ();
      //PixelOffsetMode temp = g.PixelOffsetMode;
      g.PixelOffsetMode = PixelOffsetMode.Half;
      g.SmoothingMode = SmoothingMode.None;
      g.InterpolationMode = InterpolationMode.NearestNeighbor;
      g.DrawImage (image, x, y, image.Width * zoom, image.Height * zoom);
      //g.PixelOffsetMode = temp;
      g.Restore (gs);
    }

    //public static void DrawImage (this Graphics g, Image image, int xSrc, int ySrc, int xDst, int yDst, int width, int height, Color colorKey) {
    //  g.DrawImage (image, xSrc, ySrc, xDst, yDst, width, height, colorKey, false);
    //}

    //public static void DrawImage (this Graphics g, Image image, int xSrc, int ySrc, int xDst, int yDst, int width, int height, Color colorKey, bool flipH) {
    //  ImageAttributes ia = new ImageAttributes ();
    //  if (colorKey != Color.Transparent)
    //    ia.SetColorKey (colorKey, colorKey);

    //  GraphicsState gs = g.Save ();
    //  g.PixelOffsetMode = PixelOffsetMode.Half;
    //  g.SmoothingMode = SmoothingMode.None;
    //  g.InterpolationMode = InterpolationMode.NearestNeighbor;
    //  g.DrawImage (image, new Rectangle (xDst, yDst, width, height), xSrc + (flipH ? width : 0), ySrc, width * (flipH ? -1 : 1), height, GraphicsUnit.Pixel, ia);
    //  //g.DrawImage (image, x + (flipH ? image.Width : 0), y, image.Width * (flipH ? -1 : 1), image.Height);
    //  g.Restore (gs);
    //}

    public static void DrawImage (this Graphics g, Image image, int xDst, int yDst, Color colorKey, bool flipH, bool flipV) {
      DrawImage (g, image, 0, 0, image.Width, image.Height, xDst, yDst, image.Width, image.Height, colorKey, flipH, flipV);
    }

    public static void DrawImage (this Graphics g, Image image, int xSrc, int ySrc, int widthSrc, int heightSrc, int xDst, int yDst, int widthDst, int heightDst, Color colorKey, Color colorize, bool flipH, bool flipV, Color colorOutline) {
      DrawImage (g, image, xSrc, ySrc, widthSrc, heightSrc, xDst - 1, yDst, widthDst, heightDst, colorKey, colorOutline, flipH, flipV);
      DrawImage (g, image, xSrc, ySrc, widthSrc, heightSrc, xDst, yDst - 1, widthDst, heightDst, colorKey, colorOutline, flipH, flipV);
      DrawImage (g, image, xSrc, ySrc, widthSrc, heightSrc, xDst + 1, yDst, widthDst, heightDst, colorKey, colorOutline, flipH, flipV);
      DrawImage (g, image, xSrc, ySrc, widthSrc, heightSrc, xDst, yDst + 1, widthDst, heightDst, colorKey, colorOutline, flipH, flipV);
      DrawImage (g, image, xSrc, ySrc, widthSrc, heightSrc, xDst, yDst, widthDst, heightDst, colorKey, colorize, flipH, flipV);
    }

    public static void DrawImage (this Graphics g, Image image, int xSrc, int ySrc, int widthSrc, int heightSrc, int xDst, int yDst, int widthDst, int heightDst, Color colorKey, bool flipH, bool flipV) {
      //ImageAttributes ia = new ImageAttributes ();
      //if (colorKey != Color.Transparent)
      //  ia.SetColorKey (colorKey, colorKey);

      //GraphicsState gs = g.Save ();
      //g.PixelOffsetMode = PixelOffsetMode.Half;
      ////g.PixelOffsetMode = PixelOffsetMode.None;
      //g.SmoothingMode = SmoothingMode.None;
      //g.InterpolationMode = InterpolationMode.NearestNeighbor;
      //if (DungeonView._drawRectangle)
      //  g.DrawRectangle (Pens.Fuchsia, new Rectangle (xDst + 1, yDst + 1, widthDst - 1, heightDst - 1));
      //////g.DrawRectangle (Pens.Red, new Rectangle (xDst, yDst, widthDst - 1, heightDst - 1));
      ////g.FillRectangle (Brushes.Purple, new Rectangle (xDst, yDst, widthDst, heightDst));
      //g.DrawImage (image, new Rectangle (xDst, yDst, widthDst, heightDst), xSrc + (flipH ? widthSrc : 0), ySrc, widthSrc * (flipH ? -1 : 1), heightSrc, GraphicsUnit.Pixel, ia);
      //g.Restore (gs);
      DrawImage (g, image, xSrc, ySrc, widthSrc, heightSrc, xDst, yDst, widthDst, heightDst, colorKey, Color.Transparent, flipH, flipV);
    }

    public static void DrawImage (this Graphics g, Image image, int xSrc, int ySrc, int widthSrc, int heightSrc, int xDst, int yDst, int widthDst, int heightDst, Color colorKey, Color colorize, bool flipH, bool flipV) {
      ImageAttributes ia = new ImageAttributes ();
      if (colorKey != Color.Transparent)
        ia.SetColorKey (colorKey, colorKey);
      if (colorize != Color.Transparent) {
        _colorMatrixColorize.Matrix00 = colorize.R / 255f;
        _colorMatrixColorize.Matrix11 = colorize.G / 255f;
        _colorMatrixColorize.Matrix22 = colorize.B / 255f;
        _colorMatrixColorize.Matrix33 = colorize.A / 255f;
        //_colorMatrixColorize.Matrix40 = 255f;
        //_colorMatrixColorize.Matrix41 = 255f;
        //_colorMatrixColorize.Matrix42 = 255f;
        //_imageAttributesColorize.SetColorMatrix (_colorMatrixColorize);
        ia.SetColorMatrix (_colorMatrixColorize);
      }

      GraphicsState gs = g.Save ();
      g.PixelOffsetMode = PixelOffsetMode.Half;
      //g.PixelOffsetMode = PixelOffsetMode.None;
      g.SmoothingMode = SmoothingMode.None;
      g.InterpolationMode = InterpolationMode.NearestNeighbor;

      ////g.DrawRectangle (Pens.Red, new Rectangle (xDst, yDst, widthDst - 1, heightDst - 1));
      //g.FillRectangle (Brushes.Purple, new Rectangle (xDst, yDst, widthDst, heightDst));
      //g.DrawImage (image, new Rectangle (x, y, image.Width, image.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, _imageAttributesColorize);
      //g.DrawImage (image, new Rectangle (xDst, yDst, widthDst, heightDst), xSrc + (flipH ? widthSrc : 0), ySrc, widthSrc * (flipH ? -1 : 1), heightSrc, GraphicsUnit.Pixel, ia);
      g.DrawImage (image, new Rectangle (xDst, yDst, widthDst, heightDst), xSrc + (flipH ? widthSrc : 0), ySrc + (flipV ? heightSrc : 0), widthSrc * (flipH ? -1 : 1), heightSrc * (flipV ? -1 : 1), GraphicsUnit.Pixel, ia);
      g.Restore (gs);
    }

    public static void DrawPoint (this Graphics g, Brush brush, int x, int y) {
      g.FillRectangle (brush, x, y, 1, 1);
    }

    public static void DrawPoint (this Graphics g, Brush brush, int x, int y, int size) {
      size--;
      g.FillRectangle (brush, x - size, y - size, 1 + 2 * size, 1 + 2 * size);
    }

    public static void DrawRectangleCrossed (this Graphics g, Pen pen, int x, int y, int width, int height) {
      g.DrawRectangle (pen, x, y, width, height);
      g.DrawLine (pen, x, y, x + width, y + height);
      g.DrawLine (pen, x, y + height, x + width, y);
    }

    public static void DrawStringOutlined (this Graphics g, string s, Font font, Brush brushFg, Brush brushBg, float x, float y) {
      if (s == "")
        return;
      RectangleF rectangleF = g.MeasureString2 (s, font);
      g.DrawString (s, font, brushBg, x - 1 - rectangleF.X, y - rectangleF.Y);
      g.DrawString (s, font, brushBg, x + 1 - rectangleF.X, y - rectangleF.Y);
      g.DrawString (s, font, brushBg, x - rectangleF.X, y - 1 - rectangleF.Y);
      g.DrawString (s, font, brushBg, x - rectangleF.X, y + 1 - rectangleF.Y);
      //g.DrawRectangle (Pens.Magenta, x, y, rectangleF.Size.Width - 1, rectangleF.Size.Height - 1);
      g.DrawString (s, font, brushFg, x - rectangleF.X, y - rectangleF.Y);
    }

    public static void DrawStringOutlinedCentered (this Graphics g, string s, Font font, Brush brushFg, Brush brushBg, float x, float y) {
      RectangleF rectangleF = g.MeasureString2 (s, font);
      g.DrawStringOutlined (s, font, brushFg, brushBg, x - (rectangleF.Size.Width / 2), y - (rectangleF.Size.Height / 2));
      //g.DrawStringOutlined (s, font, brushFg, brushBg, x, y - (rectangleF.Size.Height / 2));
    }

    public static void DrawLineOutlined (this Graphics g, Pen penFg, Pen penBg, int x1, int y1, int x2, int y2) {
      g.DrawLine (penBg, x1 - 1, y1, x2 - 1, y2);
      g.DrawLine (penBg, x1 + 1, y1, x2 + 1, y2);
      g.DrawLine (penBg, x1, y1 - 1, x2, y2 - 1);
      g.DrawLine (penBg, x1, y1 + 1, x2, y2 + 1);
      g.DrawLine (penFg, x1, y1, x2, y2);
    }

    public static AdjustableArrowCap _arrow = new AdjustableArrowCap (3.5f, 4, true);
    //private static float [] _dashPattern = new float [] { float.Epsilon, float.MaxValue };
    private static float [] _dashPatternBug = new float [] { 1f, float.MaxValue };
    public static void DrawArrowOutlinedSolid (this Graphics g, Pen penFg, Pen penBg, int x1, int y1, int x2, int y2, bool cap, bool dash) {
      penFg = new Pen (penFg.Color);
      penBg = new Pen (penBg.Color);
      Pen penFg2 = new Pen (penFg.Color);
      Pen penBg2 = new Pen (penBg.Color);
      //penFg.Width = 2f;
      //penBg.Width = 2f;
      //penFg2.Width = 2f;
      //penBg2.Width = 2f;

      penFg.DashPattern = _dashPatternBug;
      penBg.DashPattern = _dashPatternBug;
      penFg.DashStyle = DashStyle.Custom;
      penBg.DashStyle = DashStyle.Custom;
      penFg.CustomEndCap = _arrow;
      penBg.CustomEndCap = _arrow;

      g.DrawLine (penBg, x1 - 1, y1, x2 - 1, y2);
      g.DrawLine (penBg, x1 + 1, y1, x2 + 1, y2);
      g.DrawLine (penBg, x1, y1 - 1, x2, y2 - 1);
      g.DrawLine (penBg, x1, y1 + 1, x2, y2 + 1);
      g.DrawLine (penBg2, x1 - 1, y1, x2 - 1, y2);
      g.DrawLine (penBg2, x1 + 1, y1, x2 + 1, y2);
      g.DrawLine (penBg2, x1, y1 - 1, x2, y2 - 1);
      g.DrawLine (penBg2, x1, y1 + 1, x2, y2 + 1);

      g.DrawLine (penFg, x1, y1, x2, y2);
      g.DrawLine (penFg2, x1, y1, x2, y2);



      //if (cap) {
      //  penFg.CustomEndCap = _arrow;
      //  penBg.CustomEndCap = _arrow;
      //  penFg.Width = 1.01f;
      //  penBg.Width = 1.01f;
      //  //penFg.EndCap = LineCap.ArrowAnchor;
      //  //penBg.EndCap = LineCap.ArrowAnchor;
      //}
      //else {
      //  penFg.EndCap = LineCap.NoAnchor;
      //  penBg.EndCap = LineCap.NoAnchor;
      //}
      //g.DrawLineOutlined (penFg, penBg, x1, y1, x2, y2);
    }

    public static void DrawArrowOutlinedSolid (this Graphics g, Pen pen, int x1, int y1, int x2, int y2) {
      g.DrawArrowOutlinedSolid (pen, Pens.Black, x1, y1, x2, y2, true, false);
    }

    public static float [] _dashPattern = new float [] { 2f, 2f };
    public static void DrawArrowOutlined (this Graphics g, Pen penFg, Pen penBg, int x1, int y1, int x2, int y2, bool cap, bool dash) {
      penFg = new Pen (penFg.Color);
      penBg = new Pen (penBg.Color);

      if (cap) {
        penFg.CustomEndCap = _arrow;
        penBg.CustomEndCap = _arrow;
        penFg.Width = 1.01f;
        penBg.Width = 1.01f;
        //penFg.EndCap = LineCap.ArrowAnchor;
        //penBg.EndCap = LineCap.ArrowAnchor;
      }
      else {
        penFg.EndCap = LineCap.NoAnchor;
        penBg.EndCap = LineCap.NoAnchor;
      }

      if (dash) {
        penFg.DashPattern = _dashPattern;
        penBg.DashPattern = _dashPattern;
        penFg.DashStyle = DashStyle.Custom;
        penBg.DashStyle = DashStyle.Custom;
        penFg.Width = 2f;
        penBg.Width = 2f;
      }
      else {
        penFg.DashStyle = DashStyle.Solid;
        penBg.DashStyle = DashStyle.Solid;
      }

      g.DrawLineOutlined (penFg, penBg, x1, y1, x2, y2);
    }

    public static void DrawArrowOutlined (this Graphics g, Pen pen, int x1, int y1, int x2, int y2) {
      g.DrawArrowOutlined (pen, Pens.Black, x1, y1, x2, y2, true, true);
    }

    public static void DrawArrowOutlinedState (this Graphics g, Pen pen, int x1, int y1, int x2, int y2, bool state) {
      if (state)
        g.DrawArrowOutlined (pen, x1, y1, x2, y2);
      else
        g.DrawArrowOutlinedSolid (pen, x1, y1, x2, y2);
    }
  }
}
